package com.melon.crashhandler;

import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Environment;
import android.os.Process;

import java.io.File;
import java.io.FileOutputStream;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.io.Writer;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;

/**
 * UncaughtException处理类,当程序发生Uncaught异常的时候,有该类来接管程序,并记录发送错误报告.
 * Created by 李磊 on 2017/9/19.
 */

public class CrashHandler implements Thread.UncaughtExceptionHandler {

    private Context mContext;

    private static CrashHandler mInstance;
    //默认处理器
    private Thread.UncaughtExceptionHandler mDefaultHandler;

    /**保证只有一个CrashHandler实例**/
    private CrashHandler(){}

    /**
     * 获取 CrashHandler 实例 ,单例模式
     * @return CrashHandler
     */
    public static CrashHandler getInstance(){
        if (mInstance == null){
            synchronized (CrashHandler.class){
                if (mInstance == null){
                    mInstance = new CrashHandler();
                }
            }
        }
        return mInstance;
    }

    /**
     * 初始化处理器
     * @param context
     */
    public void init(Context context){
        mContext = context;
        // 获取系统默认的UncaughtException处理器
        mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler();
        // 设置该CrashHandler为程序的默认处理器
        Thread.setDefaultUncaughtExceptionHandler(this);
    }

    /**
     * 当UncaughtException发生时会转入该函数来处理
     */
    @Override
    public void uncaughtException(Thread thread, Throwable ex) {
        //1,收集错误信息 2,保存错误信息 3,上传到服务器
        if (!handleException(ex) && mDefaultHandler != null){
            //如果用户没有处理则让系统默认的异常处理器来处理
            mDefaultHandler.uncaughtException(thread,ex);
        } else {
            //已经人为处理
            try {
                Thread.sleep(1000);//延时1秒杀死进程
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //让程序强制退出
            Process.killProcess(Process.myPid());
            System.exit(1);
        }
    }

    /**
     * 自定义错误处理，收集错误信息，发送错误报告等操作均在此完成
     * @param ex
     * @return true：如果处理了该异常信息；否则返回 false
     */
    private boolean handleException(Throwable ex) {
        if (ex == null) {
            return false;
        }
        //收集设备参数,错误信息保存在本地并上传服务器
        dumpExceptionToSDCard(ex);
        return true;
    }

    /**
     * 导出异常信息到SD卡
     *
     * @param ex
     */
    private void dumpExceptionToSDCard(Throwable ex) {
        try {
            Writer writer = new StringWriter();
            PrintWriter printWriter = new PrintWriter(writer);
            ex.printStackTrace(printWriter);
            Throwable cause = ex.getCause();
            while (cause != null) {
                cause.printStackTrace(printWriter);
                cause = cause.getCause();
            }
            printWriter.close();
            final String result = writer.toString();

            File file = new File(getDiskCacheDir(mContext, "crash"), "crash.log");
            if (file.exists() && file.length() > 3 * 1024 * 1024) {     // <3M
                file.delete();
            }

            File folder = new File(getDiskCacheDir(mContext, "crash"));
            if (folder.exists() && folder.isDirectory()) {
                if (!file.exists()) {
                boolean newFile = file.createNewFile();
                if(!newFile){
                    return;
                }
            }
            } else {
               boolean mkDir = folder.mkdir();
               if (mkDir) {
                 boolean newFile = file.createNewFile();
                 if(!newFile){
                    return;
                 }
            }
        }
        FileOutputStream fos = new FileOutputStream(file, true);
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.CHINA);
        String format = sdf.format(new Date());
        //导出手机信息和异常信息
        PackageManager pm = mContext.getPackageManager();
        PackageInfo pi = pm.getPackageInfo(mContext.getPackageName(), PackageManager.GET_ACTIVITIES);

        byte[] bytes = ("异常发生时间：" + format +
                "\n" + "应用版本：" + pi.versionName +
                "\n" + "应用版本号：" + pi.versionCode +
                "\n" + "android版本号：" + Build.VERSION.RELEASE +
                "\n" + "android版本号API：" + Build.VERSION.SDK_INT +
                "\n" + "手机制造商:" + Build.MANUFACTURER +
                "\n" + "手机型号：" + Build.MODEL +
                "\n" + result +
                "\n" + "######## ~~~ T_T ~~~ ########" +
                "\n").getBytes();
        fos.write(bytes);
        fos.flush();
        fos.close();
        //将崩溃信息上传到服务器
        new Thread(new Runnable() {
            @Override
            public void run() {
                sendCrashServer(result);
            }
        }).start();
        Thread.sleep(2000);
        } catch (Exception e) {
            e.printStackTrace();
        }

    }
    //上传崩溃信息到服务器
    public void sendCrashServer(String crashInfo){

    }


    /**
     * @param context context
     * @param dirName dirName
     * @return dir
     */
    private String getDiskCacheDir(Context context, String dirName) {
        String cachePath = null;
        if ("mounted".equals(Environment.getExternalStorageState())) {
            File externalCacheDir = context.getExternalCacheDir();
            if (externalCacheDir != null) {
                cachePath = externalCacheDir.getPath();
            }
        }
        if (cachePath == null) {
            File cacheDir = context.getCacheDir();
            if ((cacheDir != null) && (cacheDir.exists())) {
                cachePath = cacheDir.getPath();
            }
        }
        //0/emulate/Android/data/data/com.***.***/crash/crash.log
        return cachePath + File.separator + dirName;
    }

}
